package pvp

import (
	"server/game/cache"
	"server/game/itypes"
	"server/game/model"
	"server/game/pkg/rank"
	"server/share"
	"server/share/config"
	"server/share/define"
	"strings"
	"time"

	"github.com/hwcer/cosgo/random"
	"github.com/hwcer/cosgo/registry"
	"github.com/hwcer/cosgo/slice"
	"github.com/hwcer/cosgo/times"
	"github.com/hwcer/updater"
	"github.com/hwcer/yyds/context"
	"github.com/hwcer/yyds/errors"
	"github.com/hwcer/yyds/players/player"
)

func init() {
	Register(&arena{})
}

/**
 * @name arena
 * 竞技场相关
 */

type arena struct {
}

func (this *arena) Caller(node *registry.Node, handle *context.Context) interface{} {
	f := node.Method().(func(*arena, *context.Context) interface{})
	return f(this, handle)
}

func (this *arena) getArenaConfigId(cycle int32) int32 {
	if _, ok := config.Data.ArenaConfig[cycle]; ok {
		return cycle
	}
	return 1
}
func (this *arena) GetOrCreateData(p *player.Player, cycle int32, autoSave bool) (r *model.RoleArena, e bool) {
	roleDoc := cache.GetRole(p.Updater)
	roleData := roleDoc.All()
	var needSave bool
	defer func() {
		if needSave && autoSave {
			roleDoc.Set("Arena", r)
		}
	}()

	if roleData.Arena == nil || roleData.Arena.Cycle == 0 {
		r = &model.RoleArena{}
		r.Reset(cycle)
		needSave = true
		return r, true
	}
	e = true
	r = roleData.Arena.Copy()
	if r.Cycle != cycle {
		if r.MaxScore == 0 {
			r.Reset(cycle)
			needSave = true
			e = true
		} else {
			e = false
		}
	}

	return
}

/**
 * @name cycle
 * 当前第几周
 */
func (this *arena) Cycle(c *context.Context) any {
	return rank.Cycle(define.RankNameArena, 0)
}

/**
 * @name Info
 * 我的赛季信息-仅仅方便数据查询，实际再ROLE中
 */
func (this *arena) Info(c *context.Context) any {
	cycle := rank.Cycle(define.RankNameArena, 0)
	v, _ := this.GetOrCreateData(c.Player, int32(cycle), true)
	return v
}

/**
 * @name setupPvpTeam
 * @param int32 id 阵容ID
 * 修改防守阵容
 */
func (this *arena) SetupPvpTeam(c *context.Context) any {
	id := c.GetInt32("id")
	if id == 0 {
		return errors.ErrArgEmpty
	}
	tc := config.Data.Team[id]
	if tc == nil {
		return errors.ErrConfigEmpty
	}
	if tc.Group != config.Data.TeamGroupKeys.JJCDEF {
		return c.Error("team group error")
	}
	teamsColl := c.Player.Collection(config.ITypeTeams)
	if i := teamsColl.Get(id); i == nil {
		return c.Error("team not found")
	}

	roleDoc := cache.GetRole(c.Player.Updater)
	roleDoc.Set("Defense", id)

	//cycle := rank.Cycle(define.RankNameArena, 0)
	//data, _ := this.GetOrCreateData(c.Player, int32(cycle), false)
	//
	//data.Defend = id
	//roleDoc.Set("Arena", data)
	return true
}

/**
 * @name data
 * 对手列表
 * @param string fields 需要额外获取玩家的基本信息
 * @tip 结果：[uid,分数，是否已经干翻，位置]
 */
func (this *arena) Data(c *context.Context) any {
	i := c.Player.Updater.Process.Get(arenaHandleUpdaterProcess)
	p := i.(*ArenaProcessMgr)
	bucket := rank.Get(define.RankNameArena)
	cycle, writable := bucket.Cycle()
	if !writable {
		return c.Error("休战期间无法获取对手列表")
	}
	_, ok := this.GetOrCreateData(c.Player, int32(cycle), true)
	if !ok {
		return c.Error("请先结算上赛季奖励")
	}

	if p.Remaining() > 0 {
		return p.Players
	}
	if err := this.refresh(c, bucket, cycle, p); err != nil {
		return err
	}
	return p.Players
}

/**
 * @name refresh
 * 手动刷新对手
 */

func (this *arena) Refresh(c *context.Context) any {
	i := c.Player.Updater.Process.Get(arenaHandleUpdaterProcess)
	p := i.(*ArenaProcessMgr)
	bucket := rank.Get(define.RankNameArena)
	cycle, writable := bucket.Cycle()
	if !writable {
		return c.Error("休战期间无法刷新")
	}
	_, ok := this.GetOrCreateData(c.Player, int32(cycle), true)
	if !ok {
		return c.Error("请先结算上赛季奖励")
	}

	arena3V3ConfigID := this.getArenaConfigId(int32(cycle))

	arenaConfig := config.Data.ArenaConfig[arena3V3ConfigID]
	if arenaConfig == nil {
		return c.Errorf(0, "config base arenaConfig is nil: %d", arena3V3ConfigID)
	}
	if arenaConfig.RefreshItem > 0 {
		items := cache.GetItems(c.Player.Updater)
		val := c.Player.Val(arenaConfig.RefreshItem)
		if val < 1 {
			return errors.ErrItemNotEnough
		}
		items.Sub(arenaConfig.RefreshItem, 1)
	}

	if err := this.refresh(c, bucket, cycle, p); err != nil {
		return err
	}

	return p.Players
}
func (this *arena) refresh(c *context.Context, bucket *rank.Bucket, cycle int64, p *ArenaProcessMgr) error {
	p.Players = nil
	exist := make(map[string]bool)
	exist[c.Uid()] = true

	me, err := bucket.ZRank(cycle, c.Uid(), true)
	if err != nil {
		return err
	}
	size, err := bucket.ZCard(cycle)
	if err != nil {
		return err
	}
	//players := []int{0, 5, 10, 15, 20, 30}
	var list []int64
	//第一个
	list = append(list, this.roll(size, me, 0, 5))
	//2,3
	list = append(list, this.roll(size, me, 10, 15))
	list = append(list, this.roll(size, me, 10, 15))
	//4,5
	list = append(list, this.roll(size, me, 20, 30))
	list = append(list, this.roll(size, me, 20, 30))
	for _, s := range list {
		rp, e := bucket.ZPlayer(cycle, s)
		if e != nil {
			return e
		} else if rp != nil {
			if !exist[rp.Uid] {
				exist[rp.Uid] = true
				p.Players = append(p.Players, &ArenaProcessPlayer{Uid: rp.Uid, Score: int32(rp.Score), Sort: int32(rp.Rank), Flag: 0})
			}
		}
	}

	//空空空
	if len(p.Players) < 5 {
		var ps []*share.Player
		if tx := db.Model(&model.Role{}).Limit(6).Order("update", -1).Find(&ps); tx.Error != nil {
			return tx.Error
		}
		for _, v := range ps {
			uid := v.Uid
			if !exist[uid] {
				exist[uid] = true
				p.Players = append(p.Players, &ArenaProcessPlayer{Uid: uid, Score: 0, Sort: -1, Flag: 0})
			}
			if len(p.Players) >= 5 {
				break
			}
		}
	}

	return nil
}

// this.library("random/Roll", Math.max(min_r, player_list[0]), Math.min(max_r, player_list[1])) + sort;
func (this *arena) roll(size int64, me *rank.Player, i1, i2 int64) int64 {
	as := []int64{-me.Rank, i1}
	s := slice.Max(as)
	ae := []int64{size - me.Rank - 2, i2}
	e := slice.Min(ae) + me.Rank
	return int64(random.Roll(int32(s), int32(e)))
}

/**
 * @name startBattle
 * @param string uid 对手ID
 * @tip 主线通关记录在role.stage里
 * 创建pvp战斗
 */

func (this *arena) StartBattle(c *context.Context) any {
	uid := c.GetString("uid")
	i := c.Player.Updater.Process.Get(arenaHandleUpdaterProcess)
	p := i.(*ArenaProcessMgr)
	bucket := rank.Get(define.RankNameArena)
	cycle, writable := bucket.Cycle()
	if !writable {
		return c.Error("休战期间无法战斗")
	}

	_, ok := this.GetOrCreateData(c.Player, int32(cycle), true)
	if !ok {
		return c.Error("请先结算上赛季奖励")
	}
	roleCache := cache.GetRole(c.Player.Updater)
	roleDoc := roleCache.All()
	if roleDoc.Defense == 0 {
		return c.Error("请设置防守阵容")
	}

	tar := p.Get(uid)
	if tar == nil {
		return c.Error("对手不存在，请先刷新")
	}
	if tar.Flag > 0 {
		return c.Error("无法重复挑战同一对手")
	}

	//
	arena3V3ConfigID := config.Data.Base["arena3V3ConfigID"]
	if arena3V3ConfigID == nil {
		return c.Error("config base arena3v3ConfigID is nil")
	}
	arenaConfig := config.Data.ArenaConfig[arena3V3ConfigID.Val]
	if arenaConfig == nil {
		return c.Errorf(0, "config base arenaConfig is nil: %d", arena3V3ConfigID)
	}
	if this.exist(c.Player, uid) {
		return false
	}
	if arenaConfig.BattleItem > 0 {
		c.Player.Sub(arenaConfig.BattleItem, 1)
	}
	c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
		p.Battle = uid
		return false
	})

	return true
}

func (this *arena) exist(p *player.Player, tar string) bool {
	i := p.Updater.Process.Get(arenaHandleUpdaterProcess)
	mgr := i.(*ArenaProcessMgr)
	return mgr.Battle == tar
}

/**
 * @name endBattle
 * @param int result 是否胜利
 * 结束战斗
 */

func (this *arena) EndBattle(c *context.Context) any {
	result := c.GetInt32("result")

	bucket := rank.Get(define.RankNameArena)
	cycle, _ := bucket.Cycle()

	roleDoc := cache.GetRole(c.Player.Updater)
	rollArena, ok := this.GetOrCreateData(c.Player, int32(cycle), false)
	if !ok {
		return c.Error("请先结算上赛季奖励")
	}

	i := c.Player.Updater.Process.Get(arenaHandleUpdaterProcess)
	p := i.(*ArenaProcessMgr)
	if p.Battle == "" {
		return c.Error("对手不存在或者赛季已经结束")
	}
	target := p.Get(p.Battle)
	if target == nil {
		return c.Error("对手不存")
	}
	//
	arena3V3ConfigID := this.getArenaConfigId(int32(cycle))
	arenaConfig := config.Data.ArenaConfig[arena3V3ConfigID]
	if arenaConfig == nil {
		return c.Errorf(0, "config arenaConfig is nil: %d", arena3V3ConfigID)
	}

	var score int32
	var addItem int32
	if result > 0 {
		target.Flag = 2
		score = arenaConfig.WinScore
		addItem = arenaConfig.WinItem
		c.Player.Emit(config.Data.Events.ArenaWinNum, 1)
	} else {
		target.Flag = 1
		score = arenaConfig.LoseScore
		addItem = arenaConfig.LoseItem
	}

	var reward []any
	if addItem > 0 {
		ret := itypes.ItemsGroup.Parse(c.Player.Updater, addItem, 1)
		for k, v := range ret {
			c.Player.Add(k, v)
			reward = append(reward, map[string]int32{"id": k, "num": v})
		}
	}

	if score != 0 {
		rollArena.Score += score
		if rollArena.Score > rollArena.MaxScore {
			rollArena.MaxScore = rollArena.Score
			//累计积分最大值成就
			c.Player.Emit(config.Data.Events.ArenaScore, rollArena.MaxScore)
		}
		if err := bucket.ZAdd(cycle, c.Uid(), int64(rollArena.Score)); err == nil {
			if ps, err := bucket.ZRank(cycle, c.Uid(), false); err == nil {
				rollArena.Rank = int32(ps.Rank)
				if rollArena.MaxRank < 0 || rollArena.Rank < rollArena.MaxRank {
					rollArena.MaxRank = rollArena.Rank
				}
			}
		}
		if v := rollArena.Upgrade(); v > 0 {
			c.Player.Emit(config.Data.Events.ArenaRank, v)
		}

		roleDoc.Set("Arena", rollArena)
		//计算新段位
	}

	c.Player.Emit(config.Data.Events.ArenaChallenge, 1)

	return map[string]interface{}{
		//"playerList": target,
		"reward": reward,
	}
}

/**
 * @name getRank
 * @param string uid 对手ID,用逗号分隔
 * 获取排名
 */

func (this *arena) GetRank(c *context.Context) any {
	s := c.GetString("uid")
	if s == "" {
		return errors.ErrArgEmpty
	}
	arr := strings.Split(s, ",")
	arr = slice.Unrepeated(arr)

	bucket := rank.Get(define.RankNameArena)
	cycle, _ := bucket.Cycle()
	ret := map[string]int64{}
	for _, v := range arr {
		if ps, err := bucket.ZRank(cycle, v, false); err == nil {
			ret[v] = ps.Rank
		} else {
			ret[v] = -1
		}
	}
	return ret
}

/**
 * @name getWeekReward
 * 获取每周奖励
 */

func (this *arena) GetWeekReward(c *context.Context) any {
	roleDoc := cache.GetRole(c.Player.Updater)
	roleAll := roleDoc.All()

	rollArena := roleAll.Arena.Copy()
	if rollArena.Cycle == 0 || rollArena.MaxScore == 0 {
		return c.Error("当前赛季没有参与战斗无法领取周奖励")
	}
	now := time.Now().Unix()
	st, et := rank.Expire(define.RankNameArena, int64(rollArena.Cycle))
	sw, _ := times.Unix(st).Weekly(0).Sign(0) //本届开始周
	var lastWeek int32
	if now > et {
		//上界最后一周
		lastWeek, _ = times.Unix(et).Weekly(0).Sign(0)
	} else {
		//本届上一周
		lastWeek, _ = times.Weekly(-1).Sign(0) //上周
	}
	if lastWeek < sw {
		return c.Error("暂时没有奖励可领")
	}
	if lastWeek <= rollArena.Week {
		return c.Error("上周奖励已经领取")
	}
	rollArena.Week = lastWeek
	roleDoc.Set("Arena", rollArena)

	arenaRankConfig := config.Data.ArenaRankConfig
	if arenaRankConfig == nil {
		return c.Errorf(0, "config arenaRankConfig is nil")
	}
	for _, rc := range arenaRankConfig {
		if rollArena.Score >= rc.RankStart && rollArena.Score <= rc.RankEnd {
			c.Player.Add(rc.WeeklyReward, 1)
		}
	}
	return true
}

/**
 * @name getSeasonReward
 * 获取赛季奖励
 */

func (this *arena) GetSeasonReward(c *context.Context) any {
	cycle := int32(rank.Cycle(define.RankNameArena, 0))
	roleDoc := cache.GetRole(c.Player.Updater)
	roleAll := roleDoc.All()
	rollArena := roleAll.Arena.Copy()
	if rollArena.Cycle > cycle {
		return c.Error("赛季奖励已经发放完毕")
	}
	if rollArena.Cycle == 0 {
		rollArena.Cycle = cycle
	}

	if rank.Writable(define.RankNameArena, int64(rollArena.Cycle)) {
		return c.Error("赛季没有结束") //休战以后可以领
	}

	if rollArena.Score > 0 {
		arenaRankConfig := config.Data.ArenaRankConfig
		if arenaRankConfig == nil {
			return c.Errorf(0, "config arenaRankConfig is nil")
		}
		for _, rc := range arenaRankConfig {
			if rollArena.Score >= rc.RankStart && (rc.RankEnd == 0 || rollArena.Score <= rc.RankEnd) {
				c.Player.Add(rc.SeasonReward, 1)
			}
		}
	}
	//重置赛季，中间可能跨越多个赛季
	if rank.Writable(define.RankNameArena, int64(cycle)) {
		rollArena.Reset(cycle) //当前赛季没结束 重置成当前赛季
	} else {
		rollArena.Reset(cycle + 1) //当前赛季已经结束 重置成下赛季
	}
	roleDoc.Set("Arena", rollArena)

	return true
}
